package fileutils

import (
	"fmt"
	"os"
	"privacycheck/pkg/logging"
	"sync"
	"time"
)

// FileInfo 表示文件信息
type FileInfo struct {
	Path     string
	Size     int64
	Encoding string
	ModeTime time.Time
}

// PathToFileInfo 将文件路径转换为FileInfo结构体
// 内部整合路径存在性检查、是否为目录判断，避免重复的文件状态查询
func PathToFileInfo(path string) (FileInfo, error) {
	// 一次os.Stat调用获取所有文件基本信息（替代原PathExists的功能）
	fileStat, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return FileInfo{}, fmt.Errorf("file %s not exists", path)
		}
		// 其他错误（如权限问题）
		return FileInfo{}, fmt.Errorf("obtain file %s info error: %w", path, err)
	}

	// 判断是否为目录（原PathExists的isDir判断）
	if fileStat.IsDir() {
		return FileInfo{}, fmt.Errorf("file %s is a directory", path)
	}

	// 检测文件编码（保持原逻辑）
	encoding, err := DetectFileEncoding(path)
	if err != nil {
		logging.Warnf("detect file %s encoding error:%v", path, err)
	}

	// 组装并返回FileInfo
	return FileInfo{
		Path:     path,
		Size:     fileStat.Size(),
		Encoding: encoding,
		ModeTime: fileStat.ModTime(),
	}, nil
}

// ConvertPathsToInfos 并发处理文件路径的同时支持指定最大并发线程数（goroutine 数量
func ConvertPathsToInfos(filePaths []string, concurrencyLimit int) ([]FileInfo, error) {
	resultChan := make(chan struct {
		index    int
		fileInfo FileInfo
	})
	errChan := make(chan error, len(filePaths))
	sem := make(chan struct{}, concurrencyLimit) // 控制并发数的信号量

	var wg sync.WaitGroup

	for i, path := range filePaths {
		wg.Add(1)
		go func(i int, path string) {
			defer wg.Done()
			sem <- struct{}{}        // 获取一个并发槽
			defer func() { <-sem }() // 释放并发槽

			fileInfo, err := PathToFileInfo(path)
			if err != nil {
				errChan <- err
				return
			}
			resultChan <- struct {
				index    int
				fileInfo FileInfo
			}{i, fileInfo}
		}(i, path)
	}

	// 启动一个 goroutine 等待所有任务完成
	go func() {
		wg.Wait()
		close(resultChan)
		close(errChan)
	}()

	// 收集结果（保持顺序）
	results := make([]FileInfo, len(filePaths))
	for r := range resultChan {
		results[r.index] = r.fileInfo
	}

	// 检查是否有错误
	var firstErr error
	select {
	case err := <-errChan:
		firstErr = err
	default:
	}

	return results, firstErr
}
